use num_bigint::BigInt;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ProtoFile<'input> {
    pub nodes: Vec<ProtoNode<'input>>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ProtoNode<'input> {
    Match(MatchNode<'input>),
    Chain(ChainNode<'input>),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct MatchNode<'input> {
    pub name: &'input str,
    pub r#type: TypeNode,
    pub items: Vec<MatchItem<'input>>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct MatchItem<'input> {
    pub value: LiteralNode,
    pub proto: &'input str,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ChainNode<'input> {
    pub name: &'input str,
    pub cases: Vec<CaseNode<'input>>,
    pub body: BlockNode<'input>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CaseNode<'input> {
    pub name: &'input str,
    pub value: LiteralNode,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BlockNode<'input> {
    pub nodes: Vec<RuleNode<'input>>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RuleNode<'input> {
    Def(DefNode<'input>),
    Let(LetNode<'input>),
    If(IfNode<'input>),
    Jump(JumpNode<'input>),
    Stop,
    Record(RecordNode<'input>)
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DefNode<'input> {
    pub public: bool,
    pub name: &'input str,
    pub range: RangeNode,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct LetNode<'input> {
    pub public: bool,
    pub name: &'input str,
    pub expr: ExpressionNode<'input>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct IfNode<'input> {
    pub expr: ExpressionNode<'input>,
    pub block: BlockNode<'input>,
    pub else_block: Option<BlockNode<'input>>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct JumpNode<'input> {
    pub target: JumpTarget<'input>,
    pub range: Option<RangeNode>,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RecordNodeType {
    Ipv4,
    Port,
    Number,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RecordContentItem<'input> {
    pub label: &'input str,
    pub r#type: RecordNodeType,
    pub expr: ExpressionNode<'input>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RecordNode<'input> {
    pub label: &'input str,
    pub content: Vec<RecordContentItem<'input>>,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum JumpTarget<'input> {
    Chain(&'input str),
    Proto(&'input str),
    Match(&'input str, ExpressionNode<'input>),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RangeNode {
    pub start: u16,
    pub reverse: bool,
    pub length: u16,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TypeNode {
    Int(u8),
    Bool,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum UnaryOperationNode {
    LogicNot,
    Not,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LogicalBinaryOperationNode {
    LogicAnd,
    LogicOr,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum EqualityBinaryOperationNode {
    Equals,
    NotEquals,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum MathematicsBinaryOperationNode {
    Add,
    Minus,
    Multiply,
    Divide,
    Mod,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BitOperationBinaryOperationNode {
    And,
    Or,
    Xor,
    LeftShift,
    RightShift,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum CompareBinaryOperationNode {
    Greater,
    Lesser,
    GreaterEquals,
    LesserEquals,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BinaryOperationNode {
    Logical(LogicalBinaryOperationNode),
    Equality(EqualityBinaryOperationNode),
    Mathematics(MathematicsBinaryOperationNode),
    BitOperation(BitOperationBinaryOperationNode),
    Compare(CompareBinaryOperationNode),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ExpressionNode<'input> {
    Unary {
        op: UnaryOperationNode,
        expr: Box<ExpressionNode<'input>>,
    },
    Binary {
        op: BinaryOperationNode,
        left: Box<ExpressionNode<'input>>,
        right: Box<ExpressionNode<'input>>,
    },
    Literal(LiteralNode),
    Symbol(SymbolNode<'input>),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LiteralNode {
    Int(BigInt),
    Bool(bool),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SymbolNode<'input> {
    pub name: &'input str,
}
